#include "Rasterizer.h"
#include <time.h>
#include <math.h>

#include <highgui.h>

AbstractRasterizer::AbstractRasterizer()
	:	mCurrentFaceIdx(0)
{}

AbstractRasterizer::~AbstractRasterizer()
{
	while(!mMeshes.empty()){
		delete mMeshes[0];
		mMeshes.erase(mMeshes.begin()+0);
	}
}
void AbstractRasterizer::beginMesh(const std::string& name)
{
	mCurrentMesh = new Mesh();
	mCurrentMesh->name = name;
	mCurrentMesh->transform = Mat4x4::IDENTITY;
}
Mesh* AbstractRasterizer::endMesh()
{
	mMeshes.push_back(mCurrentMesh);
	mCurrentMesh->vertices.clear();
	return mCurrentMesh;
}

void AbstractRasterizer::addVertex(const Vertex& v)
{
	mCurrentMesh->vertices.push_back(v);
}
void AbstractRasterizer::addVertex(const Vec4& p, const Color4& c, const Vec4& n, const Vec2& t){
	Vertex v;
	v.c = c;
	v.n = n;
	v.t = t;
	v.p = p;
	addVertex(v);
}
void AbstractRasterizer::addIndex(size_t idx)
{
	mCurrentFace.v[mCurrentFaceIdx++] = mCurrentMesh->vertices[idx];
	if (mCurrentFaceIdx >= 3){
		mCurrentFaceIdx = 0;
		mCurrentMesh->faces.push_back(mCurrentFace);
		mCurrentFace = Face();
	}
}
Mesh* AbstractRasterizer::addMesh(const std::string& name, objLoader& obj, const Vec3& loc, const Vec3& rot, const Vec3& sca)
{
	
	std::vector<Vec4> vertex_buffer, normal_buffer, color_buffer;
	std::vector<Vec2> textureCoord_buffer;
	size_t vertex_offset = vertex_buffer.size();
	size_t color_offset = color_buffer.size();
	size_t normal_offset = normal_buffer.size();
	size_t tex_offset = textureCoord_buffer.size();

	//resize all Buffer
	vertex_buffer.resize(vertex_offset + obj.vertexCount);
	normal_buffer.resize(normal_offset + obj.normalCount);
	color_buffer.resize(color_offset + 1);
	textureCoord_buffer.resize(tex_offset + obj.textureCount);

	//Color is not read so set default color
	color_buffer[color_offset] = Color4(1,1,1,1);

	//insert into buffer
	int vidx = vertex_offset;
	for (int i = 0; i < obj.vertexCount; i++, vidx++)
		vertex_buffer[vidx] = Vec4(obj.vertexList[i]->e[0], obj.vertexList[i]->e[1], obj.vertexList[i]->e[2]);	
	int nidx = normal_offset;
	for (int i = 0; i < obj.normalCount; i++, nidx++)
		normal_buffer[nidx] = Vec4(obj.normalList[i]->e[0], obj.normalList[i]->e[1], obj.normalList[i]->e[2]);
	int tidx = tex_offset;
	for (int i = 0; i < obj.textureCount; i++, tidx++)
		textureCoord_buffer[tidx] = Vec2(obj.textureList[i]->e[0], obj.textureList[i]->e[1]);
	
	//create meshes
	Mesh* mesh = new Mesh();
	mesh->name = name;
	mesh->transform = Mat4x4::IDENTITY;
	
	Matrix3 m;
	m.FromEulerAnglesXYZ(Radian(rot.x), Radian(rot.y), Radian(rot.z));
	Quaternion q(m);
	mesh->transform.makeTransform(loc, sca, q);
	mesh->faces.resize(obj.faceCount);
	for (int i = 0; i < obj.faceCount; i++)
	{
		for (int j = 0; j < 3; j++){
			mesh->faces[i].v[j].p = vertex_buffer[obj.faceList[i]->vertex_index[j]];
			mesh->faces[i].v[j].c = Color4(1,1,1,1);
			if (obj.faceList[i]->normal_index[j] == -1 && obj.normalCount == obj.vertexCount)
				mesh->faces[i].v[j].n = normal_buffer[obj.faceList[i]->vertex_index[j] + vertex_offset];	
			else
				mesh->faces[i].v[j].n =  normal_buffer[obj.faceList[i]->normal_index[j] + normal_offset];
			if (obj.faceList[i]->texture_index[j] == -1)
				mesh->faces[i].v[j].t = Vec2(0,0);
			else
				mesh->faces[i].v[j].t = textureCoord_buffer[obj.faceList[i]->texture_index[j] + tex_offset];
		}
	}
	mMeshes.push_back(mesh);
	return mesh;
}

void AbstractRasterizer::setTransform(IRasterizer::TRANSFORM t, const Mat4x4& m)
{
	if (t == VIEW){
		mShaderGlobals.view = m;
	}
	else if (t == PROJECTION)
		mShaderGlobals.proj = m;

}

RenderTarget* AbstractRasterizer::createRenderTarget(int w, int h)
{
	RenderTarget* s = new RenderTarget;
	s->colorBuffer = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, COLOR_BUFFER_CHANNELS);
	s->depthBuffer = cvCreateImage(cvSize(w, h), IPL_DEPTH_16U, 1);
	s->width = w;
	s->height = h;
	return s;
}

Texture* AbstractRasterizer::loadTexture(const std::string& path)
{
	IplImage* img = cvLoadImage(path.c_str());
	if (img == NULL)
		return NULL;
	Surface* s = new Surface;
	s->colorBuffer = img;
	s->width = img->width;
	s->height = img->height;
	return s;
}

#include <QFile>
#include <QCoreApplication>
#include <QTextStream>
#include <QDataStream>


template<typename STREAM>
inline STREAM& operator << (STREAM& s, const Vec2& f)
{
	s << f.x << QString(";");
	s << f.y;
	return s;
}
template<typename STREAM>
inline STREAM& operator >> (STREAM& s, Vec2& f)
{
	QString t;
	s >> f.x >> t ;
	s >> f.y;
	return s;
}
template<typename STREAM>
inline STREAM& operator << (STREAM& s, const Vec4& f)
{
	s << f.x << QString(";");
	s << f.y << QString(";");
	s << f.z << QString(";");
	s << f.w;
	return s;
}
template<typename STREAM>
inline STREAM& operator >> (STREAM& s, Vec4& f)
{
	QString t;
	s >> f.x >> t;
	s >> f.y >> t;
	s >> f.z >> t;
	s >> f.w;
	return s;
}
template<typename STREAM>
inline STREAM& operator << (STREAM& s, const Vertex& v)
{
	s << QString("P:") << v.p;
	s << QString("N:") << v.n;
	s << QString("C:") << v.c;
	s << QString("T:") << v.t;
	return s;
}
template<typename STREAM>
inline STREAM& operator >> (STREAM& s, Vertex& v)
{
	QString t;
	s >> t >> v.p;
	s >> t >> v.n;
	s >> t >> v.c;
	s >> t >> v.t;
	return s;
}


template<typename STREAM>
inline STREAM& operator << (STREAM& s, const Face& f)
{
	s << f.v[0];
	s << f.v[1];
	s << f.v[2] << QString("\n");
	return s;
}
template<typename STREAM>
inline STREAM& operator >> (STREAM& s, Face& f)
{
	s >> f.v[0];
	s >> f.v[1];
	QString t;
	s >> f.v[2] >> t;
	return s;
}

template<typename STREAM>
inline STREAM& operator << (STREAM& s, const Material& f)
{
	s << f.ambient;
	s << f.diffuse;
	s << f.glossy;
	s << f.name.c_str();
	s << f.reflect;
	s << f.refract;
	s << f.refract_index;
	s << f.shiny;
	s << f.specular;
	s << f.trans;
	s << f.texture;
	return s;
}


template<typename STREAM>
inline STREAM& operator >> (STREAM& s, Material& f)
{
	QString n;
	s >> f.ambient;
	s >> f.diffuse;
	s >> f.glossy;
	s >> n;
	f.name = std::string(n.toUtf8().data());
	s >> f.reflect;
	s >> f.refract;
	s >> f.refract_index;
	s >> f.shiny;
	s >> f.specular;
	s >> f.trans;
//	s >> f.texture;
	return s;
}



void Mesh::writeToFile(const std::string& path)
{
	QFile file(path.c_str());
    file.open(QIODevice::WriteOnly);
	QDataStream out(&file);
	out << QString("SR-Mesh\n");
	out << name.c_str() << QString("\n");
	out << QString("Faces: ");
	out << faces.size() << QString("\n");
	for (size_t i = 0; i < faces.size(); i++){
		out << faces[i];
	}
	out << material;
	
 
    // optional, as QFile destructor will already do it:
    file.close(); 
}

void Mesh::loadFromFile(const std::string& path)
{
	QFile file(path.c_str());
	if (!file.open(QIODevice::ReadOnly))
		return ;
	QDataStream in(&file);
	QString tmp;
	in >> tmp;
	in >> tmp;
	name = std::string(tmp.toUtf8().data());
	in >> tmp;
	size_t num = 0;
	in >> num;
	faces.resize(num);
	for (size_t i = 0; i < faces.size(); i++){
		in >> faces[i];
	}
	in >> material;
	file.close();
}
 
double AbstractRasterizer::render(){
	clock_t t = clock();
	int64 s = cvGetTickCount();
	prepareForRender();

	for (size_t i = 0; i < mMeshes.size(); i++){
		mShaderGlobals.material = &(mMeshes[i]->material);
		renderMesh(*mMeshes[i]);
	}
	int64 e = cvGetTickCount();
	t = clock() - t;
	double time_ms = (double)t / (double)CLOCKS_PER_SEC;
	time_ms *= 1e3;
	int64 d = e-s;
	std::cout << "Ticks: " << d << std::endl;
	return time_ms;
}

